﻿#include "context.h"

using namespace xy;

int Renderer::_gen_id() noexcept
{
    static int id = 0;
    return id++;
}

Renderer::Renderer(Context *ctx) : m_id(_gen_id()), m_ctx(ctx)
{
    if (m_ctx != nullptr)
        m_ctx->gen_array(m_id);
}

Renderer::~Renderer()
{
    m_ctx->remove_array(m_id);
}

void Renderer::update_uniform(const object *obj) const
{
    // 传入变换矩阵
    m_ctx->shader()->set_value("model", mat4x4f::identity());
    m_ctx->shader()->set_value("view", m_ctx->context()->msg()->camera->view());
    m_ctx->shader()->set_value("projection", m_ctx->context()->msg()->camera->projection());
    m_ctx->shader()->set_value("viewPos", m_ctx->context()->msg()->camera->position());

    if (obj->status() == object::Status::Hovered)
        m_ctx->shader()->set_value("alpha", hoveredOpacity);
    else if (obj->status() == object::Status::Pressed)
        m_ctx->shader()->set_value("alpha", pressedOpacity);
    else
        m_ctx->shader()->set_value("alpha", basicOpacity);
}

PointRenderer::PointRenderer(Context *ctx) : Renderer(ctx)
{
    // 生成着色器
    m_ctx->gen_shader("point");
}

void PointRenderer::update(const object *obj)
{
    m_ctx->bind_array(m_id);

    // 注意转换为 float 类型
    auto castObj = static_cast<const point *>(obj);
    vec3f pos = convert<float>(*castObj->pnt());

    glBufferData(GL_ARRAY_BUFFER, sizeof(vec3f), pos.data(), GL_DYNAMIC_DRAW);

    // vertex Positions
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vec3f), (void *)0);

    m_ctx->unbind_array();
}

void PointRenderer::draw(const object *obj) const
{
    m_ctx->bind_shader("point");

    // 外观设置
    update_uniform(obj);

    auto castObj = static_cast<const point *>(obj);
    if (castObj->status() == object::Status::Hovered)
    {
        m_ctx->shader()->set_value("color", hoveredColor);
        glPointSize(hoveredSize);
    }
    else if (castObj->status() == object::Status::Pressed)
    {
        m_ctx->shader()->set_value("color", pressedColor);
        glPointSize(pressedSize);
    }
    else
    {
        m_ctx->shader()->set_value("color", basicColor);
        glPointSize(basicSize);
    }

    // 配置绘制信息，允许后绘制的内容覆盖之前的内容
    glDepthFunc(GL_LEQUAL);

    // 开始绘制
    m_ctx->bind_array(m_id);

    glDrawArrays(GL_POINTS, 0, 1);

    m_ctx->unbind_array();

    // 恢复默认
    glPointSize(1);
    glDepthFunc(GL_LESS);
}

ArrowRenderer::ArrowRenderer(Context *ctx) : Renderer(ctx)
{
    // 生成着色器，带有几何着色器
    m_ctx->gen_shader("arrow", true);
}

void ArrowRenderer::update(const object *obj)
{
    m_ctx->bind_array(m_id);

    // 注意转换为 float 类型
    auto castObj = static_cast<const arrow *>(obj);
    vec3f origin = convert<float>(*castObj->origin());
    vec3f vec = convert<float>(*castObj->vector());

    // 创建空的缓冲
    glBufferData(GL_ARRAY_BUFFER, 2 * sizeof(vec3f), NULL, GL_DYNAMIC_DRAW);

    // 分批填充
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vec3f), origin.data());
    glBufferSubData(GL_ARRAY_BUFFER, sizeof(vec3f), sizeof(vec3f), vec.data());

    // 原点位置
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vec3f), (void *)0);

    // 向量方向
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(vec3f), (void *)(sizeof(vec3f)));

    m_ctx->unbind_array();
}

void ArrowRenderer::draw(const object *obj) const
{
    m_ctx->bind_shader("arrow");

    // 传入变换矩阵
    update_uniform(obj);

    // 向量方向作为颜色
    auto castObj = static_cast<const arrow *>(obj);
    vec3f color = convert<float>(*castObj->vector()).normalize();
    color.x() = std::abs(color.x());
    color.y() = std::abs(color.y());
    color.z() = std::abs(color.z());
    m_ctx->shader()->set_value("color", color);

    // 关闭深度缓冲，防止抗锯齿产生缝隙
    // m_ctx->glEnable(GL_POLYGON_SMOOTH);
    // m_ctx->glDepthMask(GL_FALSE);
    // m_ctx->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    glDepthFunc(GL_ALWAYS);

    // 设置深度范围固定为 0，这样绘制部分深度将会为 0，避免被覆盖
    glDepthRange(0, 0);

    // 开始绘制
    m_ctx->bind_array(m_id);

    glDrawArrays(GL_POINTS, 0, 1);

    m_ctx->unbind_array();

    // 恢复默认
    glDepthFunc(GL_LESS);
    glDepthRange(0.0, 1.0);
}

CurveRenderer::CurveRenderer(Context *ctx) : Renderer(ctx)
{
    // 生成着色器
    m_ctx->gen_shader("curve");
}

void CurveRenderer::update(const object *obj)
{
    m_ctx->bind_array(m_id);

    // 注意由于辅助曲线的存在，需要更新采样数
    auto castObj = static_cast<const ais::curve *>(obj);
    auto samples = castObj->sample(sampleCount);
    sampleCount = samples.size();

    // 传送数据
    glBufferData(GL_ARRAY_BUFFER, samples.size() * sizeof(vec3f), samples.data(), GL_DYNAMIC_DRAW);

    // vertex Positions
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vec3f), (void *)0);

    m_ctx->unbind_array();
}

void CurveRenderer::draw(const object *obj) const
{
    m_ctx->bind_shader("curve");

    // 外观设置
    update_uniform(obj);

    auto castObj = static_cast<const ais::curve *>(obj);
    if (castObj->status() == object::Status::Hovered)
    {
        m_ctx->shader()->set_value("color", hoveredColor);
        glLineWidth(hoveredWidth);
    }
    else if (castObj->status() == object::Status::Pressed)
    {
        m_ctx->shader()->set_value("color", pressedColor);
        glLineWidth(pressedWidth);
    }
    else
    {
        m_ctx->shader()->set_value("color", basicColor);
        glLineWidth(basicWidth);
    }

    // 开始绘制
    m_ctx->bind_array(m_id);

    glDrawArrays(GL_LINE_STRIP, 0, sampleCount);

    m_ctx->unbind_array();

    // 恢复默认
    glLineWidth(1);
}

MeshRenderer::MeshRenderer(Context *ctx)
    : Renderer(ctx), diffuseTex(Context::Green2d), specularTex(Context::White2d), reflectTex(Context::Black2d),
      reflactTex(Context::Black2d)
{
    // 生成着色器
    m_ctx->gen_shader("mesh");
}

void MeshRenderer::update(const object *obj)
{
    m_ctx->bind_array(m_id);

    // 创建空的缓冲
    auto castObj = static_cast<const ais::mesh *>(obj);
    auto samples = castObj->mesh_data();
    m_count = samples->indices.size(); // 注意记录顶点数

    auto n = samples->vertices.size();
    glBufferData(GL_ARRAY_BUFFER, n * (sizeof(vec3f) * 2 + sizeof(vec2f)), NULL, GL_DYNAMIC_DRAW);

    // 载入顶点和索引数据
    glBufferSubData(GL_ARRAY_BUFFER, 0, n * sizeof(vec3f), samples->vertices.data());
    glBufferSubData(GL_ARRAY_BUFFER, n * sizeof(vec3f), n * sizeof(vec3f), samples->normals.data());
    glBufferSubData(GL_ARRAY_BUFFER, 2 * n * sizeof(vec3f), n * sizeof(vec2f), samples->texcoords.data());
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, samples->indices.size() * sizeof(unsigned int), samples->indices.data(),
                 GL_DYNAMIC_DRAW);
    // 巨坑：std::size_t 占用 8 字节，而 unsigned int 占用 4 字节，导致数据错位

    // vertex Positions
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vec3f), (void *)0);

    // vertex normals
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(vec3f), (void *)(n * sizeof(vec3f)));

    // vertex texture coords
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vec2f), (void *)(2 * n * sizeof(vec3f)));
    // offsetof 计算变量偏移地址

    m_ctx->unbind_array();
}

void MeshRenderer::draw(const object *obj) const
{
    m_ctx->bind_shader("mesh");

    // 外观设置
    update_uniform(obj);

    // 光照信息和纹理贴图
    m_ctx->shader()->set_value("dirLight.direction", direction);
    m_ctx->shader()->set_value("dirLight.ambient", ambient);
    m_ctx->shader()->set_value("dirLight.diffuse", diffuse);
    m_ctx->shader()->set_value("dirLight.specular", specular);

    m_ctx->activate_tex(0);
    m_ctx->bind_tex2d(diffuseTex);
    m_ctx->shader()->set_value("material.diffuse", 0);

    m_ctx->activate_tex(1);
    m_ctx->bind_tex2d(specularTex);
    m_ctx->shader()->set_value("material.specular", 1);

    m_ctx->activate_tex(2);
    m_ctx->bind_tex2d(reflectTex);
    m_ctx->shader()->set_value("material.reflect_", 2);

    m_ctx->activate_tex(3);
    m_ctx->bind_tex2d(reflactTex);
    m_ctx->shader()->set_value("material.reflact", 3);

    // 设置斑马纹
    m_ctx->shader()->set_value("zebra", zebra);

    // 开始绘制
    m_ctx->bind_array(m_id);

    glDrawElements(GL_TRIANGLES, m_count, GL_UNSIGNED_INT, 0);

    m_ctx->unbind_array();
}

SurfaceRenderer::SurfaceRenderer(Context *ctx) : MeshRenderer(ctx)
{
}

void SurfaceRenderer::update(const object *obj)
{
    auto castObj = static_cast<const ais::surface *>(obj);
    const_cast<ais::surface *>(castObj)->sample(sampleCount);
    MeshRenderer::update(obj);
}